/**
 * Copyright (c) 2021 myons Inc. All rights reserved.
 */
package com.macro.mall.common.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;

import java.time.Duration;

/**
 * @describe:  redis 序列化配置 、
 * @author: dmzmwh 、
 * @time: 2021-09-03 13:36 、
 */
@Slf4j
@Configuration
public class RedisConfiguration {

    @Autowired
    private RedisProperties redisProperties;

//    //附属业务
//    @Value("${database.secondary:1}")
//    private Integer sd;
//
//    //过期
//    @Value("${database.ex:4}")
//    private Integer ex;


    private RedisTemplate<String, Object> buildTemplate(RedisConnectionFactory connectionFactory){
        //默认的序列化策略是JDK的，所以在存储的时候键和值都会先被序列化后再存储
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);
        //1.修改键的序列化策略
        template.setKeySerializer(new StringRedisSerializer());
        //2.修改hash类型的value中key的序列化方式
        template.setHashKeySerializer(new StringRedisSerializer());
        //4.设置value的序列化策略
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);

        ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().build();
        objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance , ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
        template.setValueSerializer(jackson2JsonRedisSerializer);
        //5.修改hash类型value中value的序列化方式
        template.setHashValueSerializer(jackson2JsonRedisSerializer);

        template.afterPropertiesSet();
        return template;
    }
    /**
     *
     * @param connectionFactory
     * @return
     */
    @Bean(name = "redisTemplate")
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        int database = ((LettuceConnectionFactory) connectionFactory).getDatabase();
        log.info("默认db是{}",database);
        return buildTemplate(connectionFactory);
    }
//
//
//    /**
//     *
//     * secondary database 从属db
//     * 处理附属业务
//     * @return
//     */
//    @Bean(name = "sdExpireRedisTemplate")
//    public RedisTemplate<String, Object> sdExpireRedisTemplate() {
//        LettuceConnectionFactory lettuceConnectionFactory = sdFactory();
//        int database = lettuceConnectionFactory.getDatabase();
//        log.info("从属db是{}",database);
//        return buildTemplate(lettuceConnectionFactory);
//    }
//
//
//    /**
//     *
//     * secondary database 从属db
//     * 单独用来做过期回调的
//     * 不要往db1里边存储其他数据 只做过期回调处理
//     * @return
//     */
//    @Bean(name = "exRedisTemplate")
//    public RedisTemplate<String, Object> exRedisTemplate() {
//        LettuceConnectionFactory lettuceConnectionFactory = exFactory();
//        int database = lettuceConnectionFactory.getDatabase();
//        log.info("回调db是{}",database);
//        return buildTemplate(lettuceConnectionFactory);
//    }


    /**
     * 优先连接工厂
     * @return
     */
    @Bean
    @Primary
    public LettuceConnectionFactory factory(){
        return new LettuceConnectionFactory(getRedisConfig(redisProperties.getDatabase()), getClientConfig());
    }
//    /**
//     * redis连接工厂
//     * @return
//     */
//    @Bean
//    public LettuceConnectionFactory sdFactory(){
//        return new LettuceConnectionFactory(getRedisConfig(sd), getClientConfig());
//    }
//
//    /**
//     * redis连接工厂
//     * @return
//     */
//    @Bean
//    public LettuceConnectionFactory exFactory(){
//        return new LettuceConnectionFactory(getRedisConfig(ex), getClientConfig());
//    }

    private RedisStandaloneConfiguration getRedisConfig(int dbNo) {
        RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
        config.setHostName(redisProperties.getHost());
        config.setPort(redisProperties.getPort());
        config.setPassword(redisProperties.getPassword());
        config.setDatabase(dbNo);
        return config;
    }

    private LettuceClientConfiguration getClientConfig() {
        GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
        RedisProperties.Pool pool = redisProperties.getLettuce().getPool();
        poolConfig.setMaxTotal(pool.getMaxActive());
        poolConfig.setMaxIdle(pool.getMaxIdle());
        poolConfig.setMinIdle(pool.getMinIdle());
        poolConfig.setMaxWaitMillis(pool.getMaxWait().toMillis());//Jedis：通常支持 -1 表示无限等待。 Lettuce 或其他实现：可能有额外限制，不完全支持 -1。
        return LettucePoolingClientConfiguration.builder().poolConfig(poolConfig).build();
    }


//    /**
//     * 回调监听
//     * @return
//     */
//    @Bean
//    public RedisMessageListenerContainer redisMessageListenerContainer(RedisConnectionFactory connectionFactory) {
//        RedisMessageListenerContainer redisMessageListenerContainer = new RedisMessageListenerContainer();
//        redisMessageListenerContainer.setConnectionFactory(connectionFactory);
//        HashSet<PatternTopic> set = new HashSet<>();
//        set.add(new PatternTopic("__keyevent@1__:expired"));
//        redisMessageListenerContainer.addMessageListener(listener, set);
//        return redisMessageListenerContainer;
//    }



    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                // 设置缓存过期时间为10分钟
                .entryTtl(Duration.ofMinutes(10))
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
                //存储序列方式
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(genericJackson2JsonRedisSerializer))
                .disableCachingNullValues();

        return RedisCacheManager.builder(factory)
                .cacheDefaults(config)
                .transactionAware()
                .build();
    }
}